package org.fhm.zdte.common.service.servlet;

import org.fhm.substrate.annotation.Component;
import org.fhm.zdte.common.annotation.RequestBody;
import org.fhm.zdte.common.exception.ServletException;
import org.fhm.zdte.common.standard.IParametersAnnotationMatch;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * The Parameter-Value-Matcher {@link RequestBody}'s implementation.
 *
 * @author 谭波
 * @since 2024/3/22
 */
@Component("RequestBody")
public class RequestBodyMatch implements IParametersAnnotationMatch {
    @Override
    public void isValidForParameter(Parameter parameter) throws ServletException {
        Class<?> clazz = parameter.getType();
        if (
                clazz.isPrimitive()
                        || String.class.isAssignableFrom(clazz)
                        || Number.class.isAssignableFrom(clazz)
                        || Boolean.class.isAssignableFrom(clazz)
                        || Character.class.isAssignableFrom(clazz)
                        || clazz.isArray()
        )
            throw new ServletException("the annotation @RequestBody can only be used to decorate objects.");
    }

    @Override
    public void match(Map<String, Object> parametersMap, List<Object> parametersList, Parameter parameter) throws ServletException {
        try {
            Class<?> parameterType = parameter.getType();
            Object instance = parameterType.newInstance();
            Field[] fields = parameterType.getDeclaredFields();
            AccessibleObject.setAccessible(fields, true);
            for (Field field : fields) {
                String fieldName = field.getName();
                Class<?> type = field.getType();
                Object value = parametersMap.get(fieldName);
                if (Objects.nonNull(value)) {
                    if (type.isArray() && value instanceof Collection) {
                        Collection<?> collection = (Collection<?>) value;
                        Object o = Array.newInstance(type, collection.size());
                        int i = 0;
                        for (Object object : collection) {
                            Array.set(o, i, object);
                            i++;
                        }
                        field.set(instance, o);
                    } else {
                        field.set(instance, value);
                    }
                }
            }
            parametersList.add(parameterType.cast(instance));
        } catch (InstantiationException | IllegalAccessException e) {
            throw new ServletException(e.getMessage());
        }
    }
}
